home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 7
/
Apprentice-Release7.iso
/
Environments
/
PowerLisp 2.01
/
Supplemental Documentation
/
Documentation
/
Chapter 26. Loop
< prev
next >
Wrap
Text File
|
1995-03-28
|
69KB
|
1,875 lines
Common Lisp the Language, 2nd Edition
-------------------------------------------------------------------------------
26. Loop
By Jon L White
[change_begin]
PREFACE: X3J13 voted in January 1989 (LOOP-FACILITY) to adopt an extended
definition of the loop macro as a part of the forthcoming draft Common Lisp
standard.
This chapter presents the bulk of the Common Lisp Loop Facility proposal,
written by Jon L White. I have edited it only very lightly to conform to the
overall style of this book and have inserted a small number of bracketed
remarks, identified by the initials GLS. (See the Acknowledgments to this
second edition for acknowledgments to others who contributed to the Loop
Facility proposal.)
- Guy L. Steele Jr.
[change_end]
-------------------------------------------------------------------------------
* Introduction
* How the Loop Facility Works
* Parsing Loop Clauses
o Order of Execution
o Kinds of Loop Clauses
o Loop Syntax
* User Extensibility
* Loop Constructs
* Iteration Control
* End-Test Control
* Value Accumulation
* Variable Initializations
* Conditional Execution
* Unconditional Execution
* Miscellaneous Features
o Data Types
o Destructuring
-------------------------------------------------------------------------------
26.1. Introduction
[change_begin]
A loop is a series of expressions that are executed one or more times, a
process known as iteration. The Loop Facility defines a variety of useful
methods, indicated by loop keywords, to iterate and to accumulate values in a
loop.
Loop keywords are not true Common Lisp keywords; they are symbols that are
recognized by the Loop Facility and that provide such capabilities as
controlling the direction of iteration, accumulating values inside the body of
a loop, and evaluating expressions that precede or follow the loop body. If you
do not use any loop keywords, the Loop Facility simply executes the loop body
repeatedly.
[change_end]
-------------------------------------------------------------------------------
26.2. How the Loop Facility Works
[change_begin]
The driving element of the Loop Facility is the loop macro. When Lisp
encounters a loop macro call form, it invokes the Loop Facility and passes to
it the loop clauses as a list of unevaluated forms, as with any macro. The loop
clauses contain Common Lisp forms and loop keywords. The loop keywords are
recognized by their symbol name, regardless of the packages that contain them.
The loop macro translates the given form into Common Lisp code and returns the
expanded form.
The expanded loop form is one or more lambda-expressions for the local binding
of loop variables and a block and a tagbody that express a looping control
structure. The variables established in the loop construct are bound as if by
using let or lambda. Implementations can interleave the setting of initial
values with the bindings. However, the assignment of the initial values is
always calculated in the order specified by the user. A variable is thus
sometimes bound to a harmless value of the correct data type, and then later in
the prologue it is set to the true initial value by using setq.
The expanded form consists of three basic parts in the tagbody:
* The loop prologue contains forms that are executed before iteration
begins, such as initial settings of loop variables and possibly an initial
termination test.
* The loop body contains those forms that are executed during iteration,
including application-specific calculations, termination tests, and
variable stepping. Stepping is the process of assigning a variable the
next item in a series of items.
* The loop epilogue contains forms that are executed after iteration
terminates, such as code to return values from the loop.
Expansion of the loop macro produces an implicit block (named nil). Thus, the
Common Lisp macro return and the special form return-from can be used to return
values from a loop or to exit a loop.
Within the executable parts of loop clauses and around the entire loop form,
you can still bind variables by using the Common Lisp special form let.
[change_end]
-------------------------------------------------------------------------------
26.3. Parsing Loop Clauses
[change_begin]
The syntactic parts of a loop construct are called clauses; the scope of each
clause is determined by the top-level parsing of that clause's keyword. The
following example shows a loop construct with six clauses:
(loop for i from 1 to (compute-top-value) ;First clause
while (not (unacceptable i)) ;Second clause
collect (square i) ;Third clause
do (format t "Working on ~D now" i) ;Fourth clause
when (evenp i) ;Fifth clause
do (format t "~D is a non-odd number" i)
finally (format t "About to exit!")) ;Sixth clause
Each loop keyword introduces either a compound loop clause or a simple loop
clause that can consist of a loop keyword followed by a single Lisp form. The
number of forms in a clause is determined by the loop keyword that begins the
clause and by the auxiliary keywords in the clause. The keywords do, initially,
and finally are the only loop keywords that can take any number of Lisp forms
and group them as if in a single progn form.
Loop clauses can contain auxiliary keywords, which are sometimes called
prepositions. For example, the first clause in the preceding code includes the
prepositions from and to, which mark the value from which stepping begins and
the value at which stepping ends.
[change_end]
-------------------------------------------------------------------------------
* Order of Execution
* Kinds of Loop Clauses
* Loop Syntax
-------------------------------------------------------------------------------
26.3.1. Order of Execution
[change_begin]
With the exceptions listed below, clauses are executed in the loop body in the
order in which they appear in the source. Execution is repeated until a clause
terminates the loop or until a Common Lisp return, go, or throw form is
encountered. The following actions are exceptions to the linear order of
execution:
* All variables are initialized first, regardless of where the establishing
clauses appear in the source. The order of initialization follows the
order of these clauses.
* The code for any initially clauses is collected into one progn in the
order in which the clauses appear in the source. The collected code is
executed once in the loop prologue after any implicit variable
initializations.
* The code for any finally clauses is collected into one progn in the order
in which the clauses appear in the source. The collected code is executed
once in the loop epilogue before any implicit values from the accumulation
clauses are returned. Explicit returns anywhere in the source, however,
will exit the loop without executing the epilogue code.
* A with clause introduces a variable binding and an optional initial
value. The initial values are calculated in the order in which the with
clauses occur.
* Iteration control clauses implicitly perform the following actions:
o initializing variables
o stepping variables, generally between each execution of the loop
body
o performing termination tests, generally just before the execution of
the loop body
[change_end]
-------------------------------------------------------------------------------
26.3.2. Kinds of Loop Clauses
[change_begin]
Loop clauses fall into one of the following categories:
* variable initialization and stepping
o The for and as constructs provide iteration control clauses that
establish a variable to be initialized. You can combine for and as
clauses with the loop keyword and to get parallel initialization and
stepping.
o The with construct is similar to a single let clause. You can
combine with clauses using and to get parallel initialization.
o The repeat construct causes iteration to terminate after a specified
number of times. It uses an internal variable to keep track of the
number of iterations.
You can specify data types for loop variables (see section 26.12.1). It is
an error to bind the same variable twice in any variable-binding clause of
a single loop expression. Such variables include local variables,
iteration control variables, and variables found by destructuring.
* value accumulation
o The collect construct takes one form in its clause and adds the
value of that form to the end of a list of values. By default, the
list of values is returned when the loop finishes.
o The append construct takes one form in its clause and appends the
value of that form to the end of a list of values. By default, the
list of values is returned when the loop finishes.
o The nconc construct is similar to append, but its list values are
concatenated as if by the Common Lisp function nconc. By default, the
list of values is returned when the loop finishes.
o The sum construct takes one form in its clause that must evaluate to
a number and adds that number into a running total. By default, the
cumulative sum is returned when the loop finishes.
o The count construct takes one form in its clause and counts the
number of times that the form evaluates to a non-nil value. By
default, the count is returned when the loop finishes.
o The minimize construct takes one form in its clause and determines
the minimum value obtained by evaluating that form. By default, the
minimum value is returned when the loop finishes.
o The maximize construct takes one form in its clause and determines
the maximum value obtained by evaluating that form. By default, the
maximum value is returned when the loop finishes.
* termination conditions
o The loop-finish Lisp macro terminates iteration and returns any
accumulated result. If specified, any finally clauses are evaluated.
o The for and as constructs provide a termination test that is
determined by the iteration control clause.
o The repeat construct causes termination after a specified number of
iterations.
o The while construct takes one form, a condition, and terminates the
iteration if the condition evaluates to nil. A while clause is
equivalent to the expression (if (not condition) (loop-finish)).
o The until construct is the inverse of while; it terminates the
iteration if the condition evaluates to any non-nil value. An until
clause is equivalent to the expression (if condition (loop-finish)).
o The always construct takes one form and terminates the loop if the
form ever evaluates to nil; in this case, it returns nil. Otherwise,
it provides a default return value of t.
o The never construct takes one form and terminates the loop if the
form ever evaluates to non-nil; in this case, it returns nil.
Otherwise, it provides a default return value of t.
o The thereis construct takes one form and terminates the loop if the
form ever evaluates to non-nil; in this case, it returns that value.
* unconditional execution
o The do construct simply evaluates all forms in its clause.
o The return construct takes one form and returns its value. It is
equivalent to the clause do (return value).
* conditional execution
o The if construct takes one form as a predicate and a clause that is
executed when the predicate is true. The clause can be a value
accumulation, unconditional, or another conditional clause; it can
also be any combination of such clauses connected by the loop keyword
and.
o The when construct is a synonym for if.
o The unless construct is similar to when except that it complements
the predicate; it executes the following clause if the predicate is
false.
o The else construct provides an optional component of if, when, and
unless clauses that is executed when the predicate is false. The
component is one of the clauses described under if.
o The end construct provides an optional component to mark the end of
a conditional clause.
* miscellaneous operations
o The named construct assigns a name to a loop construct.
o The initially construct causes its forms to be evaluated in the loop
prologue, which precedes all loop code except for initial settings
specified by the constructs with, for, or as.
o The finally construct causes its forms to be evaluated in the loop
epilogue after normal iteration terminates. An unconditional clause
can also follow the loop keyword finally.
[change_end]
-------------------------------------------------------------------------------
26.3.3. Loop Syntax
[change_begin]
The following syntax description provides an overview of the syntax for loop
clauses. Detailed syntax descriptions of individual clauses appear in sections
26.6 through 26.12. A loop consists of the following types of clauses:
initial-final ::= initially | finally
variables ::= with | initial-final | for-as | repeat
main ::= unconditional | accumulation | conditional
| termination | initial-final
loop ::= (loop [named name] {variables}* {main}*)
Note that a loop must have at least one clause; however, for backward
compatibility, the following format is also supported:
(loop {tag | expr}*)
where expr is any Common Lisp expression that can be evaluated, and tag is any
symbol not identifiable as a loop keyword. Such a format is roughly equivalent
to the following one:
(loop do {tag | expr}*)
A loop prologue consists of any automatic variable initializations prescribed
by the variable clauses, along with any initially clauses in the order they
appear in the source.
A loop epilogue consists of finally clauses, if any, along with any implicit
return value from an accumulation clause or an end-test clause.
[change_end]
-------------------------------------------------------------------------------
26.4. User Extensibility
[change_begin]
There is currently no specified portable method for users to add extensions to
the Loop Facility. The names defloop and define-loop-method have been suggested
as candidates for such a method.
[change_end]
-------------------------------------------------------------------------------
26.5. Loop Constructs
[change_begin]
The remaining sections of this chapter describe the constructs that the Loop
Facility provides. The descriptions are organized according to the
functionality of the constructs. Each section begins with a general discussion
of a particular operation; it then presents the constructs that perform the
operation.
* Section 26.6, ``Iteration Control,'' describes iteration control clauses
that allow directed loop iteration.
* Section 26.7, ``End-Test Control,'' describes clauses that stop iteration
by providing a conditional expression that can be tested after each
execution of the loop body.
* Section 26.8, ``Value Accumulation,'' describes constructs that
accumulate values during iteration and return them from a loop. This
section also discusses ways in which accumulation clauses can be combined
within the Loop Facility.
* Section 26.9, ``Variable Initializations,'' describes the with construct,
which provides local variables for use within the loop body, and other
constructs that provide local variables.
* Section 26.10, ``Conditional Execution,'' describes how to execute loop
clauses conditionally.
* Section 26.11, ``Unconditional Execution,'' describes the do and return
constructs. It also describes constructs that are used in the loop
prologue and loop epilogue.
* Section 26.12, ``Miscellaneous Features,'' discusses loop data types and
destructuring. It also presents constructs for naming a loop and for
specifying initial and final actions.
[change_end]
-------------------------------------------------------------------------------
26.6. Iteration Control
[change_begin]
Iteration control clauses allow you to direct loop iteration. The loop keywords
as, for, and repeat designate iteration control clauses.
Iteration control clauses differ with respect to the specification of
termination conditions and the initialization and stepping of loop variables.
Iteration clauses by themselves do not cause the Loop Facility to return
values, but they can be used in conjunction with value-accumulation clauses to
return values (see section 26.8).
All variables are initialized in the loop prologue. The scope of the variable
binding is lexical unless it is proclaimed special; thus, the variable can be
accessed only by expressions that lie textually within the loop. Stepping
assignments are made in the loop body before any other expressions are
evaluated in the body.
The variable argument in iteration control clauses can be a destructuring list.
A destructuring list is a tree whose non-null atoms are symbols that can be
assigned a value (see section 26.12.2).
The iteration control clauses for, as, and repeat must precede any other loop
clauses except initially, with, and named, since they establish variable
bindings. When iteration control clauses are used in a loop, termination tests
in the loop body are evaluated before any other loop body code is executed.
If you use multiple iteration clauses to control iteration, variable
initialization and stepping occur sequentially by default. You can use the and
construct to connect two or more iteration clauses when sequential binding and
stepping are not necessary. The iteration behavior of clauses joined by and is
analogous to the behavior of the Common Lisp macro do relative to do*.
[X3J13 voted in March 1989 (LOOP-AND-DISCREPANCY) to correct a minor
inconsistency in the original syntactic specification for loop. Only for and as
clauses (not repeat clauses) may be joined by the and construct. The precise
syntax is as follows.
for-as ::= {for | as} for-as-subclause {and for-as-subclause}*
for-as-subclause ::= for-as-arithmetic | for-as-in-list
| for-as-on-list | for-as-equals-then
| for-as-across | for-as-hash | for-as-package
for-as-arithmetic ::= var [type-spec] [{from | downfrom | upfrom} expr1 ]
[{to | downto | upto | below | above} expr2]
[by expr3]
for-as-in-list ::= var [type-spec] in expr1 [by step-fun]
for-as-on-list ::= var [type-spec] on expr1 [by step-fun]
for-as-equals-then ::= var [type-spec] = expr1 [then step-fun]
for-as-across ::= var [type-spec] across vector
for-as-hash ::= var [type-spec] being {each | the}
{hash-key | hash-keys | hash-value | hash-values}
{in | of} hash-table
[using ({hash-value | hash-key} other-var)]
for-as-package ::= var [type-spec] being {each | the}
for-as-package-keyword
{in | of} package
for-as-package-keyword ::= symbol | present-symbol | external-symbol
| symbols | present-symbols | external-symbols
This correction made for and as clauses syntactically similar to with clauses.
I have changed all examples in this chapter to reflect the corrected
syntax.-GLS]
In the following example, the variable x is stepped before y is stepped; thus,
the value of y reflects the updated value of x:
(loop for x from 1 to 9
for y = nil then x
collect (list x y))
=> ((1 NIL) (2 2) (3 3) (4 4) (5 5) (6 6) (7 7) (8 8) (9 9))
In the following example, x and y are stepped in parallel:
(loop for x from 1 to 9
and y = nil then x
collect (list x y))
=> ((1 NIL) (2 1) (3 2) (4 3) (5 4) (6 5) (7 6) (8 7) (9 8))
The for and as clauses iterate by using one or more local loop variables that
are initialized to some value and that can be modified or stepped after each
iteration. For these clauses, iteration terminates when a local variable
reaches some specified value or when some other loop clause terminates
iteration. At each iteration, variables can be stepped by an increment or a
decrement or can be assigned a new value by the evaluation of an expression.
Destructuring can be used to assign initial values to variables during
iteration.
The for and as keywords are synonyms and may be used interchangeably. There are
seven syntactic representations for these constructs. In each syntactic
description, the data type of var can be specified by the optional type-spec
argument. If var is a destructuring list, the data type specified by the
type-spec argument must appropriately match the elements of the list (see
sections 26.12.1 and 26.12.2).
[Loop Clause]
for var [type-spec] [{from | downfrom | upfrom} expr1]
[{to | downto | upto | below | above} expr2]
[by expr3]
as var [type-spec] [{from | downfrom | upfrom} expr1]
[{to | downto | upto | below | above} expr2]
[by expr3]
[This is the first of seven for/as syntaxes.-GLS]
The for or as construct iterates from the value specified by expr1 to the value
specified by expr2 in increments or decrements denoted by expr3. Each
expression is evaluated only once and must evaluate to a number.
The variable var is bound to the value of expr1 in the first iteration and is
stepped by the value of expr3 in each succeeding iteration, or by 1 if expr3 is
not provided.
The following loop keywords serve as valid prepositions within this syntax.
from
The loop keyword from marks the value from which stepping begins, as
specified by expr1. Stepping is incremental by default. For decremental
stepping, use above or downto with expr2. For incremental stepping, the
default from value is 0.
downfrom, upfrom
The loop keyword downfrom indicates that the variable var is decreased in
decrements specified by expr3; the loop keyword upfrom indicates that var
is increased in increments specified by expr3.
to The loop keyword to marks the end value for stepping specified in expr2.
Stepping is incremental by default. For decremental stepping, use downto,
downfrom, or above with expr2.
downto, upto
The loop keyword downto allows iteration to proceed from a larger number
to a smaller number by the decrement expr3. The loop keyword upto allows
iteration to proceed from a smaller number to a larger number by the
increment expr3. Since there is no default for expr1 in decremental
stepping, you must supply a value with downto.
below, above
The loop keywords below and above are analogous to upto and downto,
respectively. These keywords stop iteration just before the value of the
variable var reaches the value specified by expr2; the end value of expr2
is not included. Since there is no default for expr1 in decremental
stepping, you must supply a value with above.
by The loop keyword by marks the increment or decrement specified by expr3.
The value of expr3 can be any positive number. The default value is 1.
At least one of these prepositions must be used with this syntax.
In an iteration control clause, the for or as construct causes termination when
the specified limit is reached. That is, iteration continues until the value
var is stepped to the exclusive or inclusive limit specified by expr2. The
range is exclusive if expr3 increases or decreases var to the value of expr2
without reaching that value; the loop keywords below and above provide
exclusive limits. An inclusive limit allows var to attain the value of expr2;
to, downto, and upto provide inclusive limits.
A common convention is to use for to introduce new iterations and as to
introduce iterations that depend on a previous iteration specification.
[However, loop does not enforce this convention, and some of the examples below
violate it. De gustibus non disputandum est.-GLS]
Examples:
;;; Print some numbers.
(loop as i from 1 to 5
do (print i)) `;Prints 5 lines
=> NIL
;;; Print every third number.
(loop for i from 10 downto 1 by 3
do (print i)) `;Prints 4 lines
10
=> NIL
;;; Step incrementally from the default starting value.
(loop as i below 5
do (print i)) `;Prints 5 lines
=> NIL
[Loop Clause]
for var [type-spec] in expr1 [by step-fun]
as var [type-spec] in expr1 [by step-fun]
[This is the second of seven for/as syntaxes.-GLS]
This construct iterates over the contents of a list. It checks for the end of
the list as if using the Common Lisp function endp. The variable var is bound
to the successive elements of the list expr1 before each iteration. At the end
of each iteration, the function step-fun is called on the list and is expected
to produce a successor list; the default value for step-fun is the cdr
function.
The for or as construct causes termination when the end of the list is reached.
The loop keywords in and by serve as valid prepositions in this syntax.
Examples:
;;; Print every item in a list.
(loop for item in '(1 2 3 4 5) do (print item)) `;Prints 5 lines
=> NIL
;;; Print every other item in a list.
(loop for item in '(1 2 3 4 5) by #'cddr
do (print item)) `;Prints 3 lines
=> NIL
;;; Destructure items of a list, and sum the x values
;;; using fixnum arithmetic.
(loop for (item . x) (t . fixnum)
in '((A . 1) (B . 2) (C . 3))
unless (eq item 'B) sum x)
=> 4
[Loop Clause]
for var [type-spec] on expr1 [by step-fun]
as var [type-spec] on expr1 [by step-fun]
[This is the third of seven for/as syntaxes.-GLS]
This construct iterates over the contents of a list. It checks for the end of
the list as if using the Common Lisp function endp. The variable var is bound
to the successive tails of the list expr1. At the end of each iteration, the
function step-fun is called on the list and is expected to produce a successor
list; the default value for step-fun is the cdr function.
The loop keywords on and by serve as valid prepositions in this syntax. The for
or as construct causes termination when the end of the list is reached.
Examples:
;;; Collect successive tails of a list.
(loop for sublist on '(a b c d)
collect sublist)
=> ((A B C D) (B C D) (C D) (D))
;;; Print a list by using destructuring with the loop keyword ON.
(loop for (item) on '(1 2 3)
do (print item)) `;Prints 3 lines
=> NIL
;;; Print items in a list without using destructuring.
(loop for item in '(1 2 3)
do (print item)) `;Prints 3 lines
=> NIL
[Loop Clause]
for var [type-spec] = expr1 [then expr2]
as var [type-spec] = expr1 [then expr2]
[This is the fourth of seven for/as syntaxes.-GLS]
This construct initializes the variable var by setting it to the result of
evaluating expr1 on the first iteration, then setting it to the result of
evaluating expr2 on the second and subsequent iterations. If expr2 is omitted,
the construct uses expr1 on the second and subsequent iterations. When expr2 is
omitted, the expanded code shows the following optimization:
;;; Sample original code:
(loop for x = expr1 then expr2 do (print x))
;;; The usual expansion:
(tagbody
(setq x expr1)
tag (print x)
(setq x expr2)
(go tag))
;;; The optimized expansion:
(tagbody
tag (setq x expr1)
(print x)
(go tag))
The loop keywords = and then serve as valid prepositions in this syntax. This
construct does not provide any termination conditions.
Example:
;;; Collect some numbers.
(loop for item = 1 then (+ item 10)
repeat 5
collect item)
=> (1 11 21 31 41)
[Loop Clause]
for var [type-spec] across vector
as var [type-spec] across vector
[This is the fifth of seven for/as syntaxes.-GLS]
This construct binds the variable var to the value of each element in the array
vector.
The loop keyword across marks the array vector; across is used as a preposition
in this syntax. Iteration stops when there are no more elements in the
specified array that can be referenced.
Some implementations might use a [user-supplied-GLS] the special form in the
vector form to produce more efficient code.
Example:
(loop for char across (the simple-string (find-message port))
do (write-char char stream))
[Loop Clause]
for var [type-spec] being {each | the}
{hash-key | hash-keys | hash-value | hash-values}
{in | of} hash-table [using ({hash-value | hash-key} other-var)]
as var [type-spec] being {each | the}
{hash-key | hash-keys | hash-value | hash-values}
{in | of} hash-table [using ({hash-value | hash-key} other-var)]
[This is the sixth of seven for/as syntaxes.-GLS]
This construct iterates over the elements, keys, and values of a hash table.
The variable var takes on the value of each hash key or hash value in the
specified hash table.
The following loop keywords serve as valid prepositions within this syntax.
being
The keyword being marks the loop method to be used, either hash-key or
hash-value.
each, the
For purposes of readability, the loop keyword each should follow the loop
keyword being when hash-key or hash-value is used. The loop keyword the is
used with hash-keys and hash-values.
hash-key, hash-keys
These loop keywords access each key entry of the hash table. If the name
hash-value is specified in a using construct with one of these loop
methods, the iteration can optionally access the keyed value. The order in
which the keys are accessed is undefined; empty slots in the hash table
are ignored.
hash-value, hash-values
These loop keywords access each value entry of a hash table. If the name
hash-key is specified in a using construct with one of these loop methods,
the iteration can optionally access the key that corresponds to the value.
The order in which the keys are accessed is undefined; empty slots in the
hash table are ignored.
using
The loop keyword using marks the optional key or the keyed value to be
accessed. It allows you to access the hash key if iterating over the hash
values, and the hash value if iterating over the hash keys.
in, of
These loop prepositions mark the hash table hash-table.
Iteration stops when there are no more hash keys or hash values to be
referenced in the specified hash table.
[Loop Clause]
for var [type-spec] being {each | the}
{symbol | present-symbol | external-symbol |
symbols | present-symbols | external-symbols}
{in | of} package
as var [type-spec] being {each | the}
{symbol | present-symbol | external-symbol |
symbols | present-symbols | external-symbols}
{in | of} package
[This is the last of seven for/as syntaxes.-GLS]
This construct iterates over the symbols in a package. The variable var takes
on the value of each symbol in the specified package.
The following loop keywords serve as valid prepositions within this syntax.
being
The keyword being marks the loop method to be used: symbol,
present-symbol, or external-symbol.
each, the
For purposes of readability, the loop keyword each should follow the loop
keyword being when symbol, present-symbol, or external-symbol is used. The
loop keyword the is used with symbols, present-symbols, and
external-symbols.
present-symbol, present-symbols
These loop methods iterate over the symbols that are present but not
external in a package. The package to be iterated over is specified in the
same way that package arguments to the Common Lisp function find-package
are specified. If you do not specify the package for the iteration, the
current package is used. If you specify a package that does not exist, an
error is signaled.
symbol, symbols
These loop methods iterate over symbols that are accessible from a given
package. The package to be iterated over is specified in the same way that
package arguments to the Common Lisp function find-package are specified.
If you do not specify the package for the iteration, the current package
is used. If you specify a package that does not exist, an error is
signaled.
external-symbol, external-symbols
These loop methods iterate over the external symbols of a package. The
package to be iterated over is specified in the same way that package
arguments to the Common Lisp function find-package are specified. If you
do not specify the package for the iteration, the current package is used.
If you specify a package that does not exist, an error is signaled.
in, of
These loop prepositions mark the package package.
Iteration stops when there are no more symbols to be referenced in the
specified package.
Example:
(loop for x being each present-symbol of "COMMON-LISP-USER"
do (print x)) `;Prints 7 lines in this example
COMMON-LISP-USER::IN
COMMON-LISP-USER::X
COMMON-LISP-USER::ALWAYS
COMMON-LISP-USER::FOO
COMMON-LISP-USER::Y
COMMON-LISP-USER::FOR
COMMON-LISP-USER::LUCID
=> NIL
[Loop Clause]
repeat expr
The repeat construct causes iteration to terminate after a specified number of
times. The loop body is executed n times, where n is the value of the
expression expr. The expr argument is evaluated one time in the loop prologue.
If the expression evaluates to zero or to a negative number, the loop body is
not evaluated.
The clause repeat n is roughly equivalent to a clause such as
for internal-variable downfrom (- n 1) to 0
but, in some implementations, the repeat construct might be more efficient.
Examples:
(loop repeat 3 `;Prints 3 lines
do (format t "What I say three times is true~%"))
What I say three times is true
What I say three times is true
What I say three times is true
=> NIL
(loop repeat -15 `;Prints nothing
do (format t "What you see is what you expect~%"))
=> NIL
[change_end]
-------------------------------------------------------------------------------
26.7. End-Test Control
[change_begin]
The loop keywords always, never, thereis, until, and while designate constructs
that use a single test condition to determine when loop iteration should
terminate.
The constructs always, never, and thereis provide specific values to be
returned when a loop terminates. Using always, never, or thereis with
value-returning accumulation clauses can produce unpredictable results. In all
other respects these constructs behave like the while and until constructs.
The macro loop-finish can be used at any time to cause normal termination. In
normal termination, finally clauses are executed and default return values are
returned.
End-test control constructs can be used anywhere within the loop body. The
termination conditions are tested in the order in which they appear.
[Loop Clause]
while expr
until expr
The while construct allows iteration to continue until the specified expression
expr evaluates to nil. The expression is re-evaluated at the location of the
while clause.
The until construct is equivalent to while (not expr). If the value of the
specified expression is non-nil, iteration terminates.
You can use while and until at any point in a loop. If a while or until clause
causes termination, any clauses that precede it in the source are still
evaluated.
Examples:
;;; A classic "while-loop".
(loop while (hungry-p) do (eat))
;;; UNTIL NOT is equivalent to WHILE.
(loop until (not (hungry-p)) do (eat))
;;; Collect the length and the items of STACK.
(let ((stack '(a b c d e f)))
(loop while stack
for item = (length stack) then (pop stack)
collect item))
=> (6 A B C D E F)
;;; Use WHILE to terminate a loop that otherwise wouldn't
;;; terminate. Note that WHILE occurs after the WHEN.
(loop for i fixnum from 3
when (oddp i) collect i
while (< i 5))
=> (3 5)
[Loop Clause]
always expr
never expr
thereis expr
The always construct takes one form and terminates the loop if the form ever
evaluates to nil; in this case, it returns nil. Otherwise, it provides a
default return value of t.
The never construct takes one form and terminates the loop if the form ever
evaluates to non-nil; in this case, it returns nil. Otherwise, it provides a
default return value of t.
The thereis construct takes one form and terminates the loop if the form ever
evaluates to non-nil; in this case, it returns that value.
If the while or until construct causes termination, control is passed to the
loop epilogue, where any finally clauses will be executed. Since always, never,
and thereis use the Common Lisp macro return to terminate iteration, any
finally clause that is specified is not evaluated.
Examples:
;;; Make sure I is always less than 11 (two ways).
;;; The FOR construct terminates these loops.
(loop for i from 0 to 10
always (< i 11))
=> T
(loop for i from 0 to 10
never (> i 11))
=> T
;;; If I exceeds 10, return I; otherwise, return NIL.
;;; The THEREIS construct terminates this loop.
(loop for i from 0
thereis (when (> i 10) i) )
=> 11
;;; The FINALLY clause is not evaluated in these examples.
(loop for i from 0 to 10
always (< i 9)
finally (print "you won't see this"))
=> NIL
(loop never t
finally (print "you won't see this"))
=> NIL
(loop thereis "Here is my value"
finally (print "you won't see this"))
=> "Here is my value"
;;; The FOR construct terminates this loop,
;;; so the FINALLY clause is evaluated.
(loop for i from 1 to 10
thereis (> i 11)
finally (print i)) `;Prints 1 line
11
=> NIL
(defstruct mountain height difficulty (why "because it is there"))
(setq everest (make-mountain :height '(2.86e-13 parsecs)))
(setq chocorua (make-mountain :height '(1059180001 microns)))
(defstruct desert area (humidity 0))
(setq sahara (make-desert :area '(212480000 square furlongs)))
`;First there is a mountain, then there is no mountain, then there is ...
(loop for x in (list everest sahara chocorua) `; -GLS
thereis (and (mountain-p x) (mountain-height x)))
=> (2.86E-13 PARSECS)
;;; If you could use this code to find a counterexample to
;;; Fermat's last theorem, it would still not return the value
;;; of the counterexample because all of the THEREIS clauses
;;; in this example return only T. Of course, this code has
;;; never been observed to terminate.
(loop for z upfrom 2
thereis
(loop for n upfrom 3 below (log z 2)
thereis
(loop for x below z
thereis
(loop for y below z
thereis (= (+ (expt x n)
(expt y n))
(expt z n))))))
[Macro]
loop-finish
The macro loop-finish terminates iteration normally and returns any accumulated
result. If specified, a finally clause is evaluated.
In most cases it is not necessary to use loop-finish because other loop control
clauses terminate the loop. Use loop-finish to provide a normal exit from a
nested condition inside a loop.
You can use loop-finish inside nested Lisp code to provide a normal exit from a
loop. Since loop-finish transfers control to the loop epilogue, using
loop-finish within a finally expression can cause infinite looping.
Implementations are allowed to provide this construct as a local macro by using
macrolet.
Examples:
;;; Print a date in February, but exclude leap day.
;;; LOOP-FINISH exits from the nested condition.
(loop for date in date-list
do (case date
(29 (when (eq month 'february)
(loop-finish))
(format t "~:@(~A~) ~A" month date))))
;;; Terminate the loop, but return the accumulated count.
(loop for i in '(1 2 3 stop-here 4 5 6)
when (symbolp i) do (loop-finish)
count i)
=> 3
;;; This loop works just as well as the previous example.
(loop for i in '(1 2 3 stop-here 4 5 6)
until (symbolp i)
count i)
=> 3
[change_end]
-------------------------------------------------------------------------------
26.8. Value Accumulation
[change_begin]
Accumulating values during iteration and returning them from a loop is often
useful. Some of these accumulations occur so frequently that special loop
clauses have been developed to handle them.
The loop keywords append, appending, collect, collecting, nconc, and nconcing
designate clauses that accumulate values in lists and return them.
The loop keywords count, counting, maximize, maximizing, minimize, minimizing,
sum, and summing designate clauses that accumulate and return numerical values.
[There is no semantic difference between the ``ing'' keywords and their
non-``ing'' counterparts. They are provided purely for the sake of stylistic
diversity among users. I happen to prefer the non-``ing'' forms-when I use loop
at all.-GLS]
The loop preposition into can be used to name the variable used to hold partial
accumulations. The variable is bound as if by the loop construct with (see
section 26.9). If into is used, the construct does not provide a default return
value; however, the variable is available for use in any finally clause.
You can combine value-returning accumulation clauses in a loop if all the
clauses accumulate the same type of data object. By default, the Loop Facility
returns only one value; thus, the data objects collected by multiple
accumulation clauses as return values must have compatible types. For example,
since both the collect and append constructs accumulate objects into a list
that is returned from a loop, you can combine them safely.
;;; Collect every name and the kids in one list by using
;;; COLLECT and APPEND.
(loop for name in '(fred sue alice joe june)
for kids in '((bob ken) () () (kris sunshine) ())
collect name
append kids)
=> (FRED BOB KEN SUE ALICE JOE KRIS SUNSHINE JUNE)
[In the preceding example, note that the items accumulated by the collect and
append clauses are interleaved in the result list, according to the order in
which the clauses were executed.-GLS]
Multiple clauses that do not accumulate the same type of data object can
coexist in a loop only if each clause accumulates its values into a different
user-specified variable. Any number of values can be returned from a loop if
you use the Common Lisp function values, as the next example shows:
;;; Count and collect names and ages.
(loop for name in '(fred sue alice joe june)
as age in '(22 26 19 20 10)
append (list name age) into name-and-age-list
count name into name-count
sum age into total-age
finally
(return (values (round total-age name-count)
name-and-age-list)))
=> 19 and (FRED 22 SUE 26 ALICE 19 JOE 20 JUNE 10)
[Loop Clause]
collect expr [into var]
collecting expr [into var]
During each iteration, these constructs collect the value of the specified
expression into a list. When iteration terminates, the list is returned.
The argument var is set to the list of collected values; if var is specified,
the loop does not return the final list automatically. If var is not specified,
it is equivalent to specifying an internal name for var and returning its value
in a finally clause. The var argument is bound as if by the construct with. You
cannot specify a data type for var; it must be of type list.
Examples:
;;; Collect all the symbols in a list.
(loop for i in '(bird 3 4 turtle (1 . 4) horse cat)
when (symbolp i) collect i)
=> (BIRD TURTLE HORSE CAT)
;;; Collect and return odd numbers.
(loop for i from 1 to 10
if (oddp i) collect i)
=> (1 3 5 7 9)
;;; Collect items into local variable, but don't return them.
(loop for i in '(a b c d) by #'cddr
collect i into my-list
finally (print my-list)) `;Prints 1 line
(A C)
=> NIL
[Loop Clause]
append expr [into var]
appending expr [into var]
nconc expr [into var]
nconcing expr [into var]
These constructs are similar to collect except that the values of the specified
expression must be lists.
The append keyword causes its list values to be concatenated into a single
list, as if they were arguments to the Common Lisp function append.
The nconc keyword causes its list values to be concatenated into a single list,
as if they were arguments to the Common Lisp function nconc. Note that the
nconc keyword destructively modifies its argument lists.
The argument var is set to the list of concatenated values; if you specify var,
the loop does not return the final list automatically. The var argument is
bound as if by the construct with. You cannot specify a data type for var; it
must be of type list.
Examples:
;;; Use APPEND to concatenate some sublists.
(loop for x in '((a) (b) ((c)))
append x)
=> (A B (C))
;;; NCONC some sublists together. Note that only lists
;;; made by the call to LIST are modified.
(loop for i upfrom 0
as x in '(a b (c))
nconc (if (evenp i) (list x) '()))
=> (A (C))
[Loop Clause]
count expr [into var] [type-spec]
counting expr [into var] [type-spec]
The count construct counts the number of times that the specified expression
has a non-nil value.
The argument var accumulates the number of occurrences; if var is specified,
the loop does not return the final count automatically. The var argument is
bound as if by the construct with.
If into var is used, the optional type-spec argument specifies a data type for
var. If there is no into variable, the optional type-spec argument applies to
the internal variable that is keeping the count. In either case it is an error
to specify a non-numeric data type. The default type is
implementation-dependent, but it must be a subtype of (or integer float).
Example:
(loop for i in '(a b nil c nil d e)
count i)
=> 5
[Loop Clause]
sum expr [into var] [type-spec]
summing expr [into var] [type-spec]
The sum construct forms a cumulative sum of the values of the specified
expression at each iteration.
The argument var is used to accumulate the sum; if var is specified, the loop
does not return the final sum automatically. The var argument is bound as if by
the construct with.
If into var is used, the optional type-spec argument specifies a data type for
var. If there is no into variable, the optional type-spec argument applies to
the internal variable that is keeping the sum. In either case it is an error to
specify a non-numeric data type. The default type is implementation-dependent,
but it must be a subtype of number.
Examples:
;;; Sum the elements of a list.
(loop for i fixnum in '(1 2 3 4 5)
sum i)
=> 15
;;; Sum a function of elements of a list.
(setq series
'(1.2 4.3 5.7))
=> (1.2 4.3 5.7)
(loop for v in series
sum (* 2.0 v))
=> 22.4
[Loop Clause]
maximize expr [into var] [type-spec]
maximizing expr [into var] [type-spec]
minimize expr [into var] [type-spec]
minimizing expr [into var] [type-spec]
The maximize construct compares the value of the specified expression obtained
during the first iteration with values obtained in successive iterations. The
maximum value encountered is determined and returned. If the loop never
executes the body, the returned value is not meaningful.
The minimize construct is similar to maximize; it determines and returns the
minimum value.
The argument var accumulates the maximum or minimum value; if var is specified,
the loop does not return the maximum or minimum automatically. The var argument
is bound as if by the construct with.
If into var is used, the optional type-spec argument specifies a data type for
var. If there is no into variable, the optional type-spec argument applies to
the internal variable that is keeping the intermediate result. In either case
it is an error to specify a non-numeric data type. The default type is
implementation-dependent, but it must be a subtype of (or integer float).
Examples:
(loop for i in '(2 1 5 3 4)
maximize i)
=> 5
(loop for i in '(2 1 5 3 4)
minimize i)
=> 1
;;; In this example, FIXNUM applies to the internal
;;; variable that holds the maximum value.
(setq series '(1.2 4.3 5.7))
=> (1.2 4.3 5.7)
(loop for v in series
maximize (round v) fixnum)
=> 6
;;; In this example, FIXNUM applies to the variable RESULT.
(loop for v float in series
minimize (round v) into result fixnum
finally (return result))
=> 1
[change_end]
-------------------------------------------------------------------------------
26.9. Variable Initializations
[change_begin]
A local loop variable is one that exists only when the Loop Facility is
invoked. At that time, the variables are declared and are initialized to some
value. These local variables exist until loop iteration terminates, at which
point they cease to exist. Implicitly variables are also established by
iteration control clauses and the into preposition of accumulation clauses.
The loop keyword with designates a loop clause that allows you to declare and
initialize variables that are local to a loop. The variables are initialized
one time only; they can be initialized sequentially or in parallel.
By default, the with construct initializes variables sequentially; that is, one
variable is assigned a value before the next expression is evaluated. However,
by using the loop keyword and to join several with clauses, you can force
initializations to occur in parallel; that is, all of the specified expressions
are evaluated, and the results are bound to the respective variables
simultaneously.
Use sequential binding for making the initialization of some variables depend
on the values of previously bound variables. For example, suppose you want to
bind the variables a, b, and c in sequence:
(loop with a = 1
with b = (+ a 2)
with c = (+ b 3)
with d = (+ c 4)
return (list a b c d))
=> (1 3 6 10)
The execution of the preceding loop is equivalent to the execution of the
following code:
(let* ((a 1)
(b (+ a 2))
(c (+ b 3))
(d (+ c 4)))
(block nil
(tagbody
next-loop (return (list a b c d))
(go next-loop)
end-loop)))
If you are not depending on the value of previously bound variables for the
initialization of other local variables, you can use parallel bindings as
follows:
(loop with a = 1
and b = 2
and c = 3
and d = 4
return (list a b c d))
=> (1 2 3 4)
The execution of the preceding loop is equivalent to the execution of the
following code:
(let ((a 1)
(b 2)
(c 3)
(d 4))
(block nil
(tagbody
next-loop (return (list a b c))
(go next-loop)
end-loop)))
[Loop Clause]
with var [type-spec] [= expr] {and var [type-spec] [= expr]}*
The with construct initializes variables that are local to a loop. The
variables are initialized one time only.
If the optional type-spec argument is specified for any variable var, but there
is no related expression expr to be evaluated, var is initialized to an
appropriate default value for its data type. For example, for the data types t,
number, and float, the default values are nil, 0, and 0.0, respectively. It is
an error to specify a type-spec argument for var if the related expression
returns a value that is not of the specified type. The optional and clause
forces parallel rather than sequential initializations.
Examples:
;;; These bindings occur in sequence.
(loop with a = 1
with b = (+ a 2)
with c = (+ b 3)
with d = (+ c 4)
return (list a b c d))
=> (1 3 6 10)
;;; These bindings occur in parallel.
(setq a 5 b 10 c 1729)
(loop with a = 1
and b = (+ a 2)
and c = (+ b 3)
and d = (+ c 4)
return (list a b c d))
=> (1 7 13 1733)
;;; This example shows a shorthand way to declare
;;; local variables that are of different types.
(loop with (a b c) (float integer float)
return (format nil "~A ~A ~A" a b c))
=> "0.0 0 0.0"
;;; This example shows a shorthand way to declare
;;; local variables that are of the same type.
(loop with (a b c) float
return (format nil "~A ~A ~A" a b c))
=> "0.0 0.0 0.0"
[change_end]
-------------------------------------------------------------------------------
26.10. Conditional Execution
[change_begin]
The loop keywords if, when, and unless designate constructs that are useful
when you want some loop clauses to operate under a specified condition.
If the specified condition is true, the succeeding loop clause is executed. If
the specified condition is not true, the succeeding clause is skipped, and
program control moves to the clause that follows the loop keyword else. If the
specified condition is not true and no else clause is specified, the entire
conditional construct is skipped. Several clauses can be connected into one
compound clause with the loop keyword and. The end of the conditional clause
can be marked with the keyword end.
[Loop Clause]
if expr clause {and clause}*
[else clause {and clause}*] [end]
when expr clause {and clause}*
[else clause {and clause}*] [end]
unless expr clause {and clause}*
[else clause {and clause}*] [end]
The constructs when and if allow conditional execution of loop clauses. These
constructs are synonyms and can be used interchangeably. [Compare this to the
macro when, which does not allow an ``else'' part.-GLS]
If the value of the test expression expr is non-nil, the expression clause1 is
evaluated. If the test expression evaluates to nil and an else construct is
specified, the statements that follow the else are evaluated; otherwise,
control passes to the next clause.
The unless construct is equivalent to when (not expr) and if (not expr). If the
value of the test expression expr is nil, the expression clause1 is evaluated.
If the test expression evaluates to non-nil and an else construct is specified,
the statements that follow the else are evaluated; otherwise, control passes to
the next clause. [Compare this to the macro unless, which does not allow an
``else'' part-or do I mean a ``then'' part?! Ugh. To prevent confusion, I
strongly recommend as a matter of style that else not be used with unless loop
clauses.-GLS]
The clause arguments must be either accumulation, unconditional, or conditional
clauses (see section 26.3.2). Clauses that follow the test expression can be
grouped by using the loop keyword and to produce a compound clause.
The loop keyword it can be used to refer to the result of the test expression
in a clause. If multiple clauses are connected with and, the it construct must
be used in the first clause in the block. Since it is a loop keyword, it may
not be used as a local variable within a loop.
If when or if clauses are nested, each else is paired with the closest
preceding when or if construct that has no associated else.
The optional loop keyword end marks the end of the clause. If this keyword is
not specified, the next loop keyword marks the end. You can use end to
distinguish the scoping of compound clauses.
;;; Group conditional clauses into a block.
(loop for i in numbers-list
when (oddp i)
do (print i)
and collect i into odd-numbers
and do (terpri)
else ;I is even
collect i into even-numbers
finally
(return (values odd-numbers even-numbers)))
;;; Collect numbers larger than 3.
(loop for i in '(1 2 3 4 5 6)
when (and (> i 3) i)
collect it) ;it refers to (and (> i 3) i)
=> (4 5 6)
;;; Find a number in a list.
(loop for i in '(1 2 3 4 5 6)
when (and (> i 3) i)
return it)
=> 4
;;; The preceding example is similar to the following one.
(loop for i in '(1 2 3 4 5 6)
thereis (and (> i 3) i))
=> 4
;;; An example of using UNLESS with ELSE (yuk).`-GLS
(loop for turtle in teenage-mutant-ninja-turtles do
(loop for x in '(joker brainiac shredder krazy-kat)
unless (evil x)
do (eat (make-pizza :anchovies t))
else unless (and (eq x 'shredder) (attacking-p x))
do (cut turtle slack);When the evil Shredder attacks,
else (fight turtle x)));those turtle boys don't cut no slack
;;; Nest conditional clauses.
(loop for i in list
when (numberp i)
when (bignump i)
collect i into big-numbers
else ;Not (bignump i)
collect i into other-numbers
else ;Not (numberp i)
when (symbolp i)
collect i into symbol-list
else ;Not (symbolp i)
(error "found a funny value in list ~S, value ~S~%"
"list i))
;;; Without the END marker, the last AND would apply to the
;;; inner IF rather than the outer one.
(loop for x from 0 to 3
do (print x)
if (zerop (mod x 2))
do (princ " a")
and if (zerop (floor x 2))
do (princ " b")
end
and do (princ " c"))
[change_end]
-------------------------------------------------------------------------------
26.11. Unconditional Execution
[change_begin]
The loop construct do (or doing) takes one or more expressions and simply
evaluates them in order.
The loop construct return takes one expression and returns its value. It is
equivalent to the clause do (return value).
[Loop Clause]
do {expr}*
doing {expr}*
The do construct simply evaluates the specified expressions wherever they occur
in the expanded form of loop.
The expr argument can be any non-atomic Common Lisp form. Each expr is
evaluated in every iteration.
The constructs do, initially, and finally are the only loop keywords that take
an arbitrary number of forms and group them as if using an implicit progn.
Because every loop clause must begin with a loop keyword, you would use the
keyword do when no control action other than execution is required.
Examples:
;;; Print some numbers.
(loop for i from 1 to 5
do (print i)) `;Prints 5 lines
=> NIL
;;; Print numbers and their squares.
;;; The DO construct applies to multiple forms.
(loop for i from 1 to 4
do (print i)
(print (* i i))) `;Prints 8 lines
16
=> NIL
[Loop Clause]
return expr
The return construct terminates a loop and returns the value of the specified
expression as the value of the loop. This construct is similar to the Common
Lisp special form return-from and the Common Lisp macro return.
The Loop Facility supports the return construct for backward compatibility with
older loop implementations. The return construct returns immediately and does
not execute any finally clause that is given.
Examples:
;;; Signal an exceptional condition.
(loop for item in '(1 2 3 a 4 5)
when (not (numberp item))
return (cerror "enter new value"
"non-numeric value: ~s"
item)) `;Signals an error
>>Error: non-numeric value: A
;;; The previous example is equivalent to the following one.
(loop for item in '(1 2 3 a 4 5)
when (not (numberp item))
do (return
(cerror "enter new value"
"non-numeric value: ~s"
item))) `;Signals an error
>>Error: non-numeric value: A
[change_end]
-------------------------------------------------------------------------------
26.12. Miscellaneous Features
[change_begin]
The Loop Facility provides the named construct to name a loop so that the
Common Lisp special form return-from can be used.
The loop keywords initially and finally designate loop constructs that cause
expressions to be evaluated before and after the loop body, respectively.
The code for any initially clauses is collected into one progn in the order in
which the clauses appeared in the loop. The collected code is executed once in
the loop prologue after any implicit variable initializations.
The code for any finally clauses is collected into one progn in the order in
which the clauses appeared in the loop. The collected code is executed once in
the loop epilogue before any implicit values are returned from the accumulation
clauses. Explicit returns in the loop body, however, will exit the loop without
executing the epilogue code.
[change_end]
-------------------------------------------------------------------------------
* Data Types
* Destructuring
-------------------------------------------------------------------------------
26.12.1. Data Types
[change_begin]
Many loop constructs take a type-spec argument that allows you to specify
certain data types for loop variables. While it is not necessary to specify a
data type for any variable, by doing so you ensure that the variable has a
correctly typed initial value. The type declaration is made available to the
compiler for more efficient loop expansion. In some implementations, fixnum and
float declarations are especially useful; the compiler notices them and emits
more efficient code.
The type-spec argument has the following syntax:
type-spec ::= of-type d-type-spec
d-type-spec ::= type-specifier | (d-type-spec . d-type-spec)
A type-specifier in this syntax can be any Common Lisp type specifier. The
d-type-spec argument is used for destructuring, as described in section
26.12.2. If the d-type-spec argument consists solely of the types fixnum,
float, t, or nil, the of-type keyword is optional. The of-type construct is
optional in these cases to provide backward compatibility; thus the following
two expressions are the same:
;;; This expression uses the old syntax for type specifiers.
(loop for i fixnum upfrom 3 ...)
;;; This expression uses the new syntax for type specifiers.
(loop for i of-type fixnum upfrom 3 ...)
[change_end]
-------------------------------------------------------------------------------
26.12.2. Destructuring
[change_begin]
Destructuring allows you to bind a set of variables to a corresponding set of
values anywhere that you can normally bind a value to a single variable. During
loop expansion, each variable in the variable list is matched with the values
in the values list. If there are more variables in the variable list than there
are values in the values list, the remaining variables are given a value of
nil. If there are more values than variables listed, the extra values are
discarded.
Suppose you want to assign values from a list to the variables a, b, and c. You
could use one for clause to bind the variable numlist to the car of the
specified expression, and then you could use another for clause to bind the
variables a, b, and c sequentially.
;;; Collect values by using FOR constructs.
(loop for numlist in '((1 2 4.0) (5 6 8.3) (8 9 10.4))
for a integer = (first numlist)
and for b integer = (second numlist)
and for c float = (third numlist)
collect (list c b a))
=> ((4.0 2 1) (8.3 6 5) (10.4 9 8))
Destructuring makes this process easier by allowing the variables to be bound
in parallel in each loop iteration. You can declare data types by using a list
of type-spec arguments. If all the types are the same, you can use a shorthand
destructuring syntax, as the second example following illustrates.
;;; Destructuring simplifies the process.
(loop for (a b c) (integer integer float) in
'((1 2 4.0) (5 6 8.3) (8 9 10.4))
collect (list c b a)))
=> ((4.0 2 1) (8.3 6 5) (10.4 9 8))
;;; If all the types are the same, this way is even simpler.
(loop for (a b c) float in
'((1.0 2.0 4.0) (5.0 6.0 8.3) (8.0 9.0 10.4))
collect (list c b a))
=> ((4.0 2.0 1.0) (8.3 6.0 5.0) (10.4 9.0 8.0))
If you use destructuring to declare or initialize a number of groups of
variables into types, you can use the loop keyword and to simplify the process
further.
;;; Initialize and declare variables in parallel
;;; by using the AND construct.
(loop with (a b) float = '(1.0 2.0)
and (c d) integer = '(3 4)
and (e f)
return (list a b c d e f))
=> (1.0 2.0 3 4 NIL NIL)
A data type specifier for a destructuring pattern is a tree of type specifiers
with the same shape as the tree of variables, with the following exceptions:
* When aligning the trees, an atom in the type specifier tree that matches
a cons in the variable tree declares the same type for each variable.
* A cons in the type specifier tree that matches an atom in the variable
tree is a non-atomic type specifer.
;;; Declare X and Y to be of type VECTOR and FIXNUM, respectively.
(loop for (x y) of-type (vector fixnum) in my-list do ...)
If nil is used in a destructuring list, no variable is provided for its place.
(loop for (a nil b) = '(1 2 3)
do (return (list a b)))
=> (1 3)
Note that nonstandard lists can specify destructuring.
(loop for (x . y) = '(1 . 2)
do (return y))
=> 2
(loop for ((a . b) (c . d))
of-type ((float . float) (integer . integer))
in '(((1.2 . 2.4) (3 . 4)) ((3.4 . 4.6) (5 . 6)))
collect (list a b c d))
=> ((1.2 2.4 3 4) (3.4 4.6 5 6))
[It is worth noting that the destructuring facility of loop predates, and
differs in some details from, that of destructuring-bind, an extension that has
been provided by many implementors of Common Lisp.-GLS]
[Loop Clause]
initially {expr}*
finally [do | doing] {expr}*
finally return expr
The initially construct causes the specified expression to be evaluated in the
loop prologue, which precedes all loop code except for initial settings
specified by constructs with, for, or as. The finally construct causes the
specified expression to be evaluated in the loop epilogue after normal
iteration terminates.
The expr argument can be any non-atomic Common Lisp form.
Clauses such as return, always, never, and thereis can bypass the finally
clause.
The Common Lisp macro return (or the return loop construct) can be used after
finally to return values from a loop. The evaluation of the return form inside
the finally clause takes precedence over returning the accumulation from
clauses specified by such keywords as collect, nconc, append, sum, count,
maximize, and minimize; the accumulation values for these pre-empted clauses
are not returned by the loop if return is used.
The constructs do, initially, and finally are the only loop keywords that take
an arbitrary number of (non-atomic) forms and group them as if by using an
implicit progn.
Examples:
;;; This example parses a simple printed string representation
;;; from BUFFER (which is itself a string) and returns the
;;; index of the closing double-quote character.
(loop initially (unless (char= (char buffer 0) #\")
(loop-finish))
for i fixnum from 1 below (string-length buffer)
when (char= (char buffer i) #\")
return i)
;;; The FINALLY clause prints the last value of I.
;;; The collected value is returned.
(loop for i from 1 to 10
when (> i 5)
collect i
finally (print i)) `;Prints 1 line
11
=> (6 7 8 9 10)
;;; Return both the count of collected numbers
;;; as well as the numbers themselves.
(loop for i from 1 to 10
when (> i 5)
collect i into number-list
and count i into number-count
finally (return (values number-count number-list)))
=> 5 and (6 7 8 9 10)
[Loop Clause]
named name
The named construct allows you to assign a name to a loop construct so that you
can use the Common Lisp special form return-from to exit the named loop.
Only one name may be assigned per loop; the specified name becomes the name of
the implicit block for the loop.
If used, the named construct must be the first clause in the loop expression,
coming right after the word loop.
Example:
;;; Just name and return.
(loop named max
for i from 1 to 10
do (print i)
do (return-from max 'done)) `;Prints 1 line
=> DONE
[change_end]
-------------------------------------------------------------------------------